using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace Tomahawk.Runtime.Screen.Controls
{
    /// <summary>
    /// This holds the common logic for all screen controls:
    ///  * child controls collection,
    ///  * load assets method,
    ///  * render method,
    ///  * docking
    /// </summary>
    public abstract class ScreenControl
    {
        private List<ScreenControl> controls;
        private float ypos;
        private float xpos;
        private float width;
        private float height;
        private DockingModes dockMode = DockingModes.TopLeft;
        private Rectangle rect;
        private ScreenControl parentControl;
        private bool visible = true;

        /// <summary>
        /// Control whether this control must be rendered or not
        /// </summary>
        public bool Visible
        {
            get { return visible; }
            set { visible = value; }
        }

        /// <summary>
        /// Add a child parent control
        /// </summary>
        public void AddChildControl(ScreenControl control)
        {
            this.controls.Add(control);
            control.setParent(this);
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public ScreenControl()
        {
            //initialize the controls array
            this.controls = new List<ScreenControl>();

            //set the viewport size as the default position/size
            this.xpos = 0; this.ypos = 0;
            this.width = Engine.Instance.GraphicDeviceManager.GraphicsDevice.Viewport.Width;
            this.height = Engine.Instance.GraphicDeviceManager.GraphicsDevice.Viewport.Height;

            //set the layer depth
            this.layerDepth = maxLayerDepth;
            maxLayerDepth += 0.001f;
            if (maxLayerDepth > 1) maxLayerDepth = 1;
        }

        /// <summary>
        /// Get the control parent object
        /// </summary>
        public ScreenControl Parent
        {
            get { return this.parentControl; }
        }

        /// <summary>
        /// Set the parent control of this object (used by the engine)
        /// </summary>
        internal void setParent(ScreenControl parent)
        {
            this.parentControl = parent;
        }

        #region "Sprite Batch Configuration"

        private SpriteBlendMode spriteBlendMode = SpriteBlendMode.AlphaBlend;
        public SpriteBlendMode SpriteBlendMode
        {
            get { return spriteBlendMode; }
            set { spriteBlendMode = value; }
        }

        private SpriteSortMode spriteSortMode = SpriteSortMode.Immediate;
        public SpriteSortMode SpriteSortMode
        {
            get { return spriteSortMode; }
            set { spriteSortMode = value; }
        }

        private SaveStateMode saveStateMode = SaveStateMode.None;
        public SaveStateMode SaveStateMode
        {
            get { return saveStateMode; }
            set { saveStateMode = value; }
        }

        #endregion

        /// <summary> 
        /// Render all child control by default
        /// </summary>
        public void Render(GraphicsDevice device, SpriteBatch spriteBatch)
        {
            //render only if visible
            if (this.visible)
            {
                //render all child controls
                foreach (ScreenControl sc in controls)
                {
                    sc.Render(device, spriteBatch);
                }

                //begin the batch rendering
                spriteBatch.Begin(spriteBlendMode, spriteSortMode, saveStateMode);

                //now render the control
                this.OnRender(device, spriteBatch);

                //end the rendering
                spriteBatch.End();

            }

        }

        /// <summary>
        /// Override this method to provide presentation logic
        /// </summary>
        protected abstract void OnRender(GraphicsDevice device, SpriteBatch spriteBatch);

        /// <summary>
        /// Load the screen control necesary assets
        /// </summary>
        public void LoadGraphicsContent(GraphicsDevice myDevice, ContentManager myLoader)
        {
            //let the object load it's contents
            this.OnLoadGraphicContent(myDevice, myLoader);

            //load assets on all child controls
            foreach (ScreenControl sc in controls)
            {
                sc.LoadGraphicsContent(myDevice, myLoader);
            }
        }

        /// <summary>
        /// Override this to provide your content loading logic
        /// </summary>
        protected abstract void OnLoadGraphicContent(GraphicsDevice myDevice, ContentManager myLoader);

        #region "Rectangle: Position & Size "

        /// <summary>
        /// Get or sets the vertical displacement of the control. 
        /// It will translate to vertical position depending on the DockMode.
        /// </summary>
        public float Y
        {
            get { return ypos; }
            set { ypos = value; }
        }

        /// <summary>
        /// Get or sets the horizontal displacement of the control. 
        /// It will translate to horizontal position depending on the DockMode.
        /// </summary>
        public float X
        {
            get { return xpos; }
            set { xpos = value; }
        }

        /// <summary>
        /// Get or set the width of the control.
        /// It may be ignored in certain DockModes
        /// </summary>
        public float Width
        {
            get { return width; }
            set { width = value; }
        }

        /// <summary>
        /// Get or set the height of the control.
        /// It may be ignored in certain DockModes
        /// </summary>
        public float Height
        {
            get { return height; }
            set { height = value; }
        }

        #endregion

        #region "Docking / Anchoring"

        /// <summary>
        /// Available docking modes. 
        /// This controls how the control is shaped against his parent.
        /// </summary>
        public enum DockingModes
        {
            Top,
            TopLeft,
            TopRight,
            Left,
            Right,
            Bottom,
            BottomLeft,
            BottomRight,
            Fill
        }

        /// <summary>
        /// Set the docking behaviour
        /// </summary>
        public DockingModes DockMode
        {
            get { return this.dockMode; }
            set { this.dockMode = value; }
        }

        /// <summary>
        /// Determine control's position ans size, using docking and parent rect.
        /// </summary>
        private void conputeRect()
        {
            //use viewport's size as default parent's size/position
            Rectangle parentRect = new Rectangle(0, 0, Engine.Instance.GraphicDeviceManager.GraphicsDevice.Viewport.Width, Engine.Instance.GraphicDeviceManager.GraphicsDevice.Viewport.Height);

            //get parent's rect (if any)
            if (this.parentControl != null) parentRect = this.parentControl.Rect;

            //compute current rect
            switch (this.dockMode)
            {
                case DockingModes.Top:
                    this.rect = new Rectangle(
                        parentRect.X,  
                        parentRect.Y,  
                        parentRect.Width, 
                        (int)this.height
                        );
                    break;
                case DockingModes.Bottom:
                    this.rect = new Rectangle(
                        parentRect.X,  
                        (int)(parentRect.Top + parentRect.Height - this.height),
                        parentRect.Width,
                        (int)this.height
                        );
                    break;
                case DockingModes.Left:
                    this.rect = new Rectangle(
                        parentRect.X,
                        parentRect.Y,                    
                        (int)this.width,   
                        parentRect.Height  
                        );
                    break;
                case DockingModes.Right:
                    this.rect = new Rectangle(
                        (int)(parentRect.Width - this.width),
                        parentRect.Y,                 
                        (int)this.width,   
                        parentRect.Height  
                        );
                    break;
                case DockingModes.Fill:
                    this.rect = parentRect;
                    break;
                case DockingModes.TopLeft:
                    this.rect = new Rectangle(
                        (int)(this.X + parentRect.X), 
                        (int)(this.Y + parentRect.Y), 
                        (int)this.width,
                        (int)this.height 
                        );
                    break;
                case DockingModes.TopRight:
                    this.rect = new Rectangle(
                        (int)((parentRect.X + parentRect.Width) - (this.width + this.xpos)),
                        (int)(this.Y + parentRect.Y),
                        (int)this.width, 
                        (int)this.height 
                        );
                    break;
                case DockingModes.BottomLeft:
                    this.rect = new Rectangle(
                        (int)(this.X + parentRect.X),  
                        (int)((parentRect.Y + parentRect.Height) - (this.height + this.ypos)),
                        (int)this.width, 
                        (int)this.height 
                        );
                    break;
                case DockingModes.BottomRight:
                    this.rect = new Rectangle(
                        (int)((parentRect.X + parentRect.Width) - (this.width + this.xpos)),
                        (int)((parentRect.Y + parentRect.Height) - (this.height + this.ypos)),
                        (int)this.width, 
                        (int)this.height 
                        );
                    break;
            }
        }

        /// <summary>
        /// Get the computed rectangle
        /// </summary>
        public Rectangle Rect
        {
            get 
            {
                this.conputeRect();
                return this.rect; 
            }
        }

        #endregion

        #region "Depth Management"

        private float layerDepth = 0.0f;

        public float LayerDepth
        {
            get { return this.layerDepth; }
            set { this.layerDepth = value; }
        }

        protected static float maxLayerDepth;

        #endregion

    }

}


